import { SpriteFrame } from 'cc';
import { DEBUG, PREVIEW } from 'cc/env';

import BundleExt from './BundleExt';
import { getDefaultLang } from './EventType';

/**
 * 多语言文本本地化控制器
 */
export default class MutiLang {

    // 当前语言
    private _curLang: string = '';
    public static get curLang(): string {
        return MutiLang.getIns()._curLang || getDefaultLang();
    }
    // 备选语言，当前配置语言找不到时，启用备选语言为默认语言
    private _subLang: string = '';
    private static get subLang(): string {
        if (MutiLang.getIns()._subLang == MutiLang.getIns()._curLang)
            return "";
        else
            return MutiLang.getIns()._subLang;
    }
    /**
     * 初始化多语言控制器
     * @param curLang 当前使用语言
     * @param subLang 备选语言
     */
    static init(curLang: string, subLang?: string) {
        MutiLang.getIns()._curLang = curLang;
        if (!!subLang)
            MutiLang.getIns()._subLang = subLang;
    }

    // 数据信息
    private _data: { [lang: string]: any } = {};

    // 单例实现
    private static _ins: MutiLang = null;
    private constructor() {
        // 开放多语言数据便于调试
        if (DEBUG)
            globalThis.MutiLang = MutiLang;
    }
    static getIns(): MutiLang {
        if (!MutiLang._ins) {
            MutiLang._ins = new MutiLang();
        }
        return MutiLang._ins;
    }

    // UI预期的多语言列表
    private _uiLangList: string[] = [];
    /**
     * 当前支持的多语言列表
     */
    static get uiLangList(): string[] {
        const limitList = MutiLang.getIns()._uiLangList;
        if (limitList && limitList.length > 0)
            return limitList;

        let list: string[] = [];
        for (const key in MutiLang.getIns()._data) {
            if (key && key.length)
                list.push(key);
        }
        return list;
    }
    /**
     * 限制切换多语言UI的语言列表
     * 不设置的话，读取所有可用的语言列表
     * @param list 手动指定可切换的语言列表
     */
    static set uiLangList(list: string[]) {
        if (list && list.length > 0) {
            MutiLang.getIns()._uiLangList.splice(0);
            list.forEach(str => {
                if (str && str.length > 0)
                    MutiLang.getIns()._uiLangList.push(str);
            });
        }
    }

    // UI是否支持指定语言
    private enableLang(lang: string): boolean {
        const list = MutiLang.uiLangList;
        // 无UI语言限制
        if (list.length == 0)
            return true;
        return list.findIndex(str => str == lang) >= 0;
    }

    /**
     * 按照指定语言，设置多语言数据
     * @param lang 语言类型字符串
     * @param jsonObj 语言对应的json文本对象
     */
    static setLangData(lang: string, jsonObj: { [key: string]: any }) {
        if (!lang || lang.length == 0)
            return;
        if (MutiLang.getIns().enableLang(lang))
            MutiLang.getIns()._data[lang] = jsonObj;
    }

    /**
     * 按照指定的key，设置多语言数据
     * @param key 多语言的key
     * @param langObj 当前key对应的文本多语言对象
     */
    static setkeyLangData(key: string, langObj: { [lang: string]: string }) {
        if (!key || key.length == 0 || !langObj)
            return;

        for (const lang in langObj) {
            if (!lang || lang.length == 0)
                continue;
            if (!MutiLang.getIns().enableLang(lang))
                continue;
            let langData = MutiLang.getIns()._data[lang];
            if (!langData)
                langData = MutiLang.getIns()._data[lang] = {};
            langData[key] = langObj[lang];
        }
    }

    /**
     * 获取指定key的转义后文本
     * @param key 键值
     */
    static getString(key: string, ...param: Array<string | number>): string {
        let content: string = MutiLang.getIns().getOriginalContent(key);
        // 查询替换参数
        if (param && param.length > 0 && content && content.length > 0) {
            for (let i = 0; i < param.length; i++) {
                const str: string = '' + param[i];
                const simple = "{&" + i + "}";
                // 无标识参数直接填到末尾
                if (content.search(simple) >= 0)
                    content = content.replace(simple, str);
                else
                    content += str;
            }
        }
        return content;
    }

    // 查找key对应的原始文本
    private getOriginalContent(key: string): string {
        if (!this._data || !key || key.length == 0) {
            DEBUG && console.debug('[MutiLang] 未配置多语言数据配置！key:', key);
            return "";
        }

        // 拆分查询路径
        const paths: string[] = key.split(".");
        // 查询function
        const search = (data: any, path: string[]): string => {
            // check input
            if (!data || !path.length) return "";
            // 遍历paths
            for (let i = 0; i < paths.length; i++) {
                if (!paths[i] || !paths[i].length) {
                    DEBUG && console.debug(`[MutiLang] 多语言键值路径异常，key:${key}`);
                    return "";
                }
                data = data[paths[i]];
                if (!data) return "";
            }
            return data || "";
        };

        // 当前语言配置
        const curLang = this._data[MutiLang.curLang];
        // 备选语言配置
        const subLang = MutiLang.subLang.length > 0 ? this._data[MutiLang.subLang] : null;
        // 优先搜索当前语言配置
        let ans = curLang ? search(curLang, paths) : "";
        if (ans.length > 0) {
            return ans;
        }
        else if (subLang) {
            ans = search(subLang, paths);
            return ans;
        }
        return "";
    }

    /**
     * 加载指定路径图片资源
     * @param resName 图片资源名
     * @param cb 加载资源后的回调
     * @returns
     */
    public static loadSprite(resName: string, cb: (spriteFrame: SpriteFrame, language: string) => void, bundleName?: string): void {
        if (!cb) return;
        resName = !!resName ? resName.trim() : '';
        const curLang = this.curLang;
        if (!curLang.length || !resName.length) {
            DEBUG && console.warn(`[MutiLang] loadSprite 处理resName:${resName}时，发生异常！`);
            cb(null, "");
            return;
        }

        // load the sprite frame of from resources folder
        const curLangPath: string = this.fixImgPath(resName, curLang);
        BundleExt.loadSprite(curLangPath, (err, frame: SpriteFrame) => {
            if (!err && !!frame) {
                cb(frame, curLang);
            }
            else if (MutiLang.subLang.length > 0 && MutiLang.subLang != curLang) {
                // 有设置备选语言，则尝试启用备选语言资源
                const extLangPath: string = this.fixImgPath(resName, MutiLang.subLang);
                BundleExt.loadSprite(extLangPath, (err2, frame2: SpriteFrame) => {
                    if (!err2 && !!frame2) {
                        cb(frame2, MutiLang.subLang);
                    }
                    else {
                        cb(null, MutiLang.subLang);
                        PREVIEW && console.warn(`[MutiLang] loadSprite bundle:${bundleName} path:[${curLangPath}] 加载备选语言[${MutiLang.subLang}]图片失败！`, err);
                    }
                }, bundleName);
            }
            else {
                cb(null, curLang);
                PREVIEW && console.warn(`[MutiLang] loadSprite bundle:${bundleName} path:[${curLangPath}] 加载主语言[${MutiLang.curLang}]、备选语言[${MutiLang.subLang}]图片全失败！`, err);
            }
        }, bundleName);
    }

    /**
     * 格式化图片资源路径
     * 备注：约定多语言图片资源固定放在包内“mutilang”文件夹内
     * @param resName 图片文件名
     * @param language 指定语言
     * @returns 
     */
    static fixImgPath(resName: string, language: string): string {
        return 'mutilang/' + resName + '_' + language;
    }

}

